#include "stdafx.h"
#include "snetwork/stdafx.h"
#include "snetwork/NluEvent.h"
#include "snetwork/NluHttpRequest.h"    
#include "snetwork/NluHttpParse.h"
#include "snetwork/NluConnection.h"
#include "snetwork/NluHttpCreate.h"
#include "standard/nhex.h"
#include "standard/CThrowException.h"
#include "standard/nstring.h"

namespace Sock
{   
    CNluHttpRequest *CNluHttpRequest::m_instance = NULL;

    CNluHttpRequest *CNluHttpRequest::instance()
    {
        if (m_instance==NULL) {
            m_instance = new CNluHttpRequest();
        }
        return m_instance;
    }
    
    CNluHttpRequest::CNluHttpRequest()
    {
        memset(m_method, 0, sizeof(m_method));
    } 
        
    int CNluHttpRequest::execute(nlu_event_t *rev)
    {
		try {		    
		    
		    //run request
		    this->HttpProcessRequestLine(rev);
		    
		    CNluHttpCreate::instance()->execute(rev);
		    //llog("r->uri_start:%s", r->uri_start); 
	    }
		catch (CThrowException e) {
			llog("Error: %s", e.getMessage().c_str());
            return -1;
		}
		return 0;
    }
    
    // process GET / HTTP/1.1
    void CNluHttpRequest::HttpProcessRequestLine(nlu_event_t *rev)
    {        
        long                n;
        int                 rc;  
        nlu_connection_t    *c;
        nlu_request_t       *r;    
        CNluHttpParse       hp;        
        
        c = rev->data;
        r = c->data;
                    
        rc = HTTP_AGAIN;        
        for ( ; ; ) {
            //llog("r->header_in->pos : %s", r->header_in->pos);
            if (rc == HTTP_AGAIN) {
                n = this->HttpReadRequestHeader(r);                
                //llog("r->header_in->pos : %s \n n: %ld", r->header_in->pos, n);//n=422
            }
            
            rc = hp.ParseRequestLine(r, r->header_in);                        
            
            if (rc == NL_HTTP_PARSE_INVALID_METHOD) {
                llog("PARSE_INVALID_METHOD");
                break;
            }
            
            if (rc == HTTP_OK) {
                HttpProcessRequestHeaders(r, r->header_in);
                break;
            }
            break;
        }        
        
        if (r->uri_end) {
            r->uri_start[r->uri_end - r->uri_start] = '\0';
            //llog("r->uri_start:%s", r->uri_start);
        }
        
        if (r->request_end) {
            r->request_start[r->request_end - r->request_start] = '\0';
            llog("r->request_start:%s", r->request_start);
        }
        
        /*
        llog("r->uri_start:%s", r->uri_start);
        //llog("r->uri_end:%s", r->uri_end);
        //llog("r->uri_ext:%s", r->uri_ext);
        llog("r->args_start:%s", r->args_start);
        llog("r->request_start:%s", r->request_start);
        //llog("r->request_end:%s", r->request_end);
        //llog("r->method_end:%s", r->method_end);
        llog("r->schema_start:%s", r->schema_start);
        //llog("r->schema_end:%s", r->schema_end);
        llog("r->host_start:%s", r->host_start);
        //llog("r->host_end:%s", r->host_end);
        llog("r->port_start:%s", r->port_start);
        //llog("r->port_end:%s", r->port_end);
        */
    }
    
    // process Host, Accept etc..
    void CNluHttpRequest::HttpProcessRequestHeaders(nl_http_request_t *r, http_buf_t *b)
    {        
        CNluHttpParse hp;        
        int rc = HTTP_AGAIN;
        
        for ( ; ; ) {
            
            rc = hp.ParseHeaderLine(b, r, 1);
            
            if (rc == HTTP_OK) {
                r->header_name_start[r->header_name_end - r->header_name_start] = '\0';
                r->header_start[r->header_end - r->header_start] = '\0';                
                continue;
            }
            
            if (rc == NL_HTTP_PARSE_HEADER_DONE) {
                HttpProcessRequestHeaders(r, b);                
                return;
            }
            
            return;
        }
    }            

    ssize_t CNluHttpRequest::HttpReadRequestHeader(nl_http_request_t *r)
    {
        ssize_t n;
        nl_connection_t *c;
        nl_event_t *rev;

        n = r->header_in->last - r->header_in->pos;
        c = r->connection;
        rev = (nl_event_t *) c->read;
        
        if (n > 0) {
            return n;
        }

        if (rev->ready) {
            n = this->UnixRecv(c, r->header_in->last,
                    r->header_in->end - r->header_in->last);
        }
        else {
            n = HTTP_AGAIN;
        }

        if (n == HTTP_AGAIN) {
            /*
            if (ngx_handle_read_event(rev, 0) != HTTP_OK) {
                ngx_http_close_request(r, HTTP_INTERNAL_SERVER_ERROR);
                return HTTP_ERROR;
            }
            */
            return HTTP_AGAIN;
        }

        if (n == 0) {
            llog("client closed prematurely connection");
        }

        if (n == 0 || n == HTTP_ERROR) {
            return HTTP_ERROR;
        }

        r->header_in->last += n;

        return n;
    }
    
    ssize_t CNluHttpRequest::UnixRecv(nl_connection_t *c, char *buf, size_t size)
    {
        ssize_t n;
        nl_event_t *rev;
        
        rev = (nl_event_t *) c->read;        

        do {
            n = recv(c->fd, buf, size, 0);            
            
            //llog("buf:\n%s", buf);

            if (n == 0) {
                rev->ready = 0;
                rev->eof = 1;
                return n;
            }
            else if (n > 0) {
                if ((size_t) n < size) {
                    rev->ready = 0;
                }
                return n;
            }

            if (errno == EAGAIN || errno == EINTR) {
                llog( "recv() not ready" );
                n = HTTP_AGAIN;
            }
            else {
                n = HTTP_ERROR;
                break;
            }

        } while (errno == EINTR);

        rev->ready = 0;

        if (n == HTTP_ERROR) {
            rev->error = 1;
        }

        return n;
    }
        
      
     //set block
    int CNluHttpRequest::SetNonBlocking(int sockfd) 
    {
#if defined(__GNUC__)
        int flags, s;

        flags = fcntl (sockfd, F_GETFL, 0);
        if (flags == -1) {
            perror ("fcntl");
            return -1;
        }

        flags |= O_NONBLOCK;
        s = fcntl (sockfd, F_SETFL, flags);
        if (s == -1) {
            perror ("fcntl");
            return -1;
        }

        return 0;
        
#elif defined(_MSC_VER)
		u_long i=1;
		ioctlsocket(sockfd,FIONBIO,&i);
		
		return 0;
#endif
    }   
    
    int CNluHttpRequest::_write_file_tmp(std::string& action, std::string& filename)
    {
        char tmp_file[] = "tmp_XXXXXX";


        int file_fd = mkstemp(tmp_file); // tmp_xysesdfaasllds
        std::ofstream writef;
        std::string recvstr = n_rbArr[0];//
        
        unsigned int file_count = n_FILES[action]["tmp_name"].size();

        n_FILES[action]["tmp_name"][file_count].append(tmp_file, sizeof(tmp_file));// n_FILES[action]["tmp_name"] = tmp_xysesdfaasllds
        n_FILES[action]["name"][file_count].append(filename.c_str(), filename.length());//n_FILES[action]["tmp_name"] = abcd.jpg
        
        writef.open(tmp_file, std::ios::binary|std::ios::trunc);//m_fileinfo["filename"].c_str()
        writef.write(recvstr.c_str(), recvstr.length());        // write to tmp name
        writef.close();
        
        close(file_fd);          
		return 0;
    }
}    
